<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Ts中unknown与any</title>
</head>
<body>
  <h5>在typescript中，当我们不确定一个类型是什么类型的，可以选择给其声明为any或者unkown。但实际上，typescript推荐使用unknown，因为unknown是类型安全的。</h5>
  <script>
    const a: unknown = 8;
    // 此刻你不知道a到底是什么类型，你基本对他做不了任何的操作。
    // console.log(a.x);
    if (a instanceof Promise) {
      a.xxxxxxx
    }
    // 如果是any，你可以任意的取值赋值，不会进行任何的类型检查。但unkown就不一样了，必须先进行断言 就是使用typeof 或 instanceof 来判断类型。
    
    // 其实二者是有本质的区别的，说起来要引入一个多态的概念。
    // 举个简单的例子，人类的祖先是猿人，他们也会生火，也会穿一些简单的衣服…
    
    // 所以猿人可以看做是现代人类的父类型，他可以替代子类型出现。在程序运行时我们可以这样写
    猿人 = new 现代人()
    // 也就是：
    parent: Parent = new Child()

    // 但是你不能让子类型去替代父类型，因为显然子类型的能力基于父类型扩展出来的，所以，子类型的方法行为父类型未必有。
    现代人 = new 猿人()
    现代人.开车()
    // 显然是跑不通的，因为现代人只是代替猿人出现的，真正的对象是猿人，他当然不会开车...
  
    // 二者均可以被任何类型赋值，但当调用他们身上方法属性的时候
    let foo: unknown = 'foo';
    let bar: any = 'bar';

    let foo1: String = foo; // not ok
    let bar1: String = bar; // ok

    foot.then(() => {}) // not ok
    bar.then(() => {}) // ok

    // 我们发现，foo(unkonw)不可以赋值给其他类型，而bar(any)可以。这里其实折射了一个本质问题：
    // any是任意类型的父类型，同时也任意类型的子类型
    // unknown是任意类型的父类型，但仅此而已。

    // any可以作为其他任意类型的子类型，也就是任意类型都可以替代any类型出现，那么，String可以，Promise也可以，所以bar(any)可以赋值给bar1(String)，同样也可以调用then方法（因为Promise代替他了）。
    // 至此，我们就知道为什么unknown是类型安全的了。因为他只是指明了类型还未确认，后续还需要你去断言，也就是他并未放弃类型检查。
  </script>
</body>
</html>